/* ===========================================================================
 *
 *	   Filename:  zinnia_handwriting.cpp
 *
 *	Description:  zinnia_handwriting (qt)
 *
 * - Mode: 644
 * - NOTE. NOT use "dos"
 * - Updated: TODO
 *
 *		Version:  1.0.0
 *		Created:  2015-04-16 10:13:08
 *	   Revision:  1.0.0
 *	   Compiler:  moc/g++
 *
 *		 Author:  Yui Wong, email: yuiwong@126.com
 *   Organization:  ""
 *		License:  LGPLv3
 *
 *  This file is part of portable-inputmethod.yw.
 *
 *  portable-inputmethod.yw
 *  is free software: you can redistribute it
 *  and/or modify it under the terms of the GNU General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  portable-inputmethod.yw
 *  is distributed in the hope that it will be
 *  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with portable-inputmethod.yw.
 *  If not, see <http://www.gnu.org/licenses/>.
 *
 * ===========================================================================
 */


#include <iostream> /* std .. */
#include <zinnia_handwriting.h> /* class zinnia_handwriting */
#include <simp_sync_mutx.h>

#if defined(PIMETHOD_DEBUG)
#	include <QtCore/QDebug>
#endif
#include <QtGui/QPainter> /* QPainter */

#if (ID_QT_VERSION < 0x050000)
#	include <QtGui/QApplication>
#else
#	include <QtWidgets/QApplication>
#endif


#define __STLST_ALL \
	"background-color: #87cefa; color: #ff0000; font-weight: bold; " \
	"border-radius: 8px"

#define ZINNIA_HW_MODEL_DFT "handwriting-zh_CN.model"
#define ZINNIA_MAXM_LEN (127)


char zinnia_handwriting::m[ZINNIA_MAXM_LEN + 1] = ZINNIA_HW_MODEL_DFT;
size_t zinnia_handwriting::writor_width_fixed = 400;
size_t zinnia_handwriting::writor_height_fixed = 400;
int zinnia_handwriting::writor_pen_w_fixed = 2;/* 4 -> 2 */


zinnia_handwriting::zinnia_handwriting (QWidget * parent)
	: inputmethod(parent) {
	int i;
	QSpacerItem * spacer;
	QPalette palette;
	QFont font;

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	/*
	 * STAGE-I: pre-init
	 */
	this->access = 1;
	this->allpage = 0;
	this->index = 0;
	this->writor_layout = NULL;
	this->option_layout = NULL;
	this->candidate_layout = NULL;
	this->focus = NULL;
	this->core_initialized = false;

	/*
	 * STAGE-I-II: deinit core
	 */
	this->do_deinit_core();
	this->recognizer = NULL;

	/*
	 * STAGE-II: ui
	 */
	/* writor */
	this->setContentsMargins(0, 0, 0, 0);
	this->setFixedSize(writor_width_fixed, writor_height_fixed);
	this->setAutoFillBackground(true);
	palette.setColor(QPalette::Background, QColor(0xff, 0xff, 0xff));
	this->setPalette(palette);

	this->master_layout= new QVBoxLayout(this);


	this->writor_layout = new QHBoxLayout();
	this->writor_layout->setContentsMargins(0, 0, 0, 0);


	this->option_layout = new QVBoxLayout();
	this->option_layout->setContentsMargins(0, 0, 0, 0);

	this->up = new QPushButton(tr("<<"), this);
	this->up->setFixedSize(36, 36);
	font.setPixelSize(14);
	this->up->setFont(font);
	this->up->setStyleSheet(__STLST_ALL);

	this->down = new QPushButton(tr(">>"), this);
	this->down->setFixedSize(36, 36);
	this->down->setFont(font);
	this->down->setStyleSheet(__STLST_ALL);

	spacer = new QSpacerItem(36, 24);
	this->option_layout->addItem(spacer);
	this->option_layout->addWidget(this->up);
	spacer = new QSpacerItem(36, 24);
	this->option_layout->addItem(spacer);
	this->option_layout->addWidget(this->down);
	spacer = new QSpacerItem(36, 24);
	this->option_layout->addItem(spacer);

	this->option_layout->setAlignment(this->up,
		Qt::AlignRight | Qt::AlignVCenter);
	this->option_layout->setAlignment(this->down,
		Qt::AlignRight | Qt::AlignVCenter);

	this->option_layout->setStretch(0, 1);
	this->option_layout->setStretch(1, 1);
	this->option_layout->setStretch(2, 1);
	this->option_layout->setStretch(3, 1);
	this->option_layout->setStretch(4, 1);


	this->candidate_layout = new QHBoxLayout();
	this->candidate_layout->setContentsMargins(5, 0, 5, 0);
	/* ready candidate_btns */
	for (i = 0; i < ZINNIA_HW_CB_NUM; ++i) {
		candidate_btns[i] = new QPushButton(this);
		candidate_btns[i]->setFixedSize(36, 36);
		candidate_btns[i]->setFont(font);
		candidate_btns[i]->setStyleSheet(__STLST_ALL);
		this->candidate_layout->addWidget(candidate_btns[i]);
	}


	spacer = new QSpacerItem(zinnia_handwriting::writor_width_fixed - 64 - 10,
		zinnia_handwriting::writor_height_fixed - 64 - 10);
	this->writor_layout->addItem(spacer);

	this->writor_layout->addLayout(this->option_layout, 1);
	this->writor_layout->setAlignment(this->option_layout, Qt::AlignCenter);

	this->master_layout->addLayout(this->writor_layout, 0);
	this->master_layout->addLayout(this->candidate_layout, 1);

	for (i = 0; i < ZINNIA_HW_CB_NUM; ++i) {
		this->candidate_layout->setAlignment(this->candidate_btns[i],
			Qt::AlignHCenter);
	}

	for (i = 0; i < ZINNIA_HW_CB_NUM; ++i) {
		QObject::connect(this->candidate_btns[i], SIGNAL(clicked()), this,
		SLOT(choose_char()));
	}
	QObject::connect(this->up, SIGNAL(clicked()), this, SLOT(turnpageup()));
	QObject::connect(this->down, SIGNAL(clicked()), this,
		SLOT(turnpagedown()));
	QObject::connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)),
		this, SLOT(restore_last_focus(QWidget*, QWidget*)));

	this->genbuttonstate();
} /* zinnia_handwriting::zinnia_handwriting */


void zinnia_handwriting::do_deinit_core (void) {
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	if (! this->core_initialized) {
#		if defined(PIMETHOD_DEBUG)
		qDebug() << __FILE__ << __LINE__ << __func__ << "NOT-NEEDED";
#		endif /* if defined(PIMETHOD_DEBUG) */
		return;
	}

	if (this->recogtimer.isActive()) {
		this->recogtimer.stop();
	}
	this->recogtimer.disconnect();

	memset(zinnia_handwriting::m, 0, ZINNIA_MAXM_LEN + 1);
	strncpy(zinnia_handwriting::m, ZINNIA_HW_MODEL_DFT, ZINNIA_MAXM_LEN);

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__ << "BEFORE: release zinnia";
#	endif /* if defined(PIMETHOD_DEBUG) */

	if (this->recognizer) {
		zinnia_recognizer_t * d = this->recognizer;
		this->recognizer = NULL;
		(void)zinnia_recognizer_close(d);
		(void)zinnia_recognizer_destroy(d);
	}

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__ << "AFTER: release zinnia";
#	endif /* if defined(PIMETHOD_DEBUG) */

	this->core_initialized = false;
} /* zinnia_handwriting::do_deinit_core */


void zinnia_handwriting::CORE_READY (void) {
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	this->core_initialized = true;
} /* zinnia_handwriting::CORE_READY */


int zinnia_handwriting::reinitialize_core (QWidget * focus,
	const char * m) {
	int retval;
	char mdmp[ZINNIA_MAXM_LEN + 1];

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	/**/capture_lock((this->access));

	/*
	 * pre-ready: deinit-core
	 */
	this->do_deinit_core();

	if (! focus) {
		this->focus = focus;
	}

	/*
	 * STAGE-III: recognizer-init
	 */
	this->recognizer = zinnia_recognizer_new();/* create recognizer engine */
	/* open model */
	if (m) {
		memset(mdmp, 0, ZINNIA_MAXM_LEN + 1);
		strncpy(mdmp, m, ZINNIA_MAXM_LEN);
#		if defined(PIMETHOD_DEBUG)
		qDebug() << __FILE__ << __LINE__ << __func__ << mdmp;
#		endif /* if defined(PIMETHOD_DEBUG) */
		retval = zinnia_recognizer_open(this->recognizer, mdmp);
	} else {
		retval = zinnia_recognizer_open(this->recognizer,
			ZINNIA_HW_MODEL_DFT);
	}
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
	perror("zinnia_recognizer_open");
#	endif /* if defined(PIMETHOD_DEBUG) */
	if (! retval) {
		/* fail */
		fprintf(stderr, "ERROR: %s: zinnia_recognizer_open fail: %s\n",
			__func__, zinnia_recognizer_strerror(this->recognizer));
		fflush(stderr);
		retval = inputmethod_manager::errno_zinnia_open_fail * -1;
		goto back;
	} /* (! retval) */

	/* ok to record it when needed */
	if (m) {
		strncpy(zinnia_handwriting::m, mdmp, ZINNIA_MAXM_LEN);
	}

	/*
	 * STAGE-III-II: timer
	 */
	/* ready to recognize: 1s */
	this->recogtimer.setInterval(1e3);
	this->recogtimer.stop();
	this->recogtimer.disconnect();
	QObject::connect(&(this->recogtimer), SIGNAL(timeout()),
		this, SLOT(recognize()));

	this->recogtimer.setInterval(1e3);
	this->recogtimer.stop();
	this->recogtimer.disconnect();
	QObject::connect(&(this->recogtimer), SIGNAL(timeout()),
		this, SLOT(recognize()));

	/* ok to set core_initialized true */
	this->CORE_READY();
	retval = 0;

back:
	rel_lock((this->access));/**/

	return  retval;
} /* zinnia_handwriting::reinitialize_core */


zinnia_handwriting::~zinnia_handwriting (void) {
	int i;

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	/**/capture_lock((this->access));

	this->do_deinit_core();

	for (i = 0; i < ZINNIA_HW_CB_NUM; ++i) {
		QObject::disconnect(this->candidate_btns[i], SIGNAL(clicked()), this,
		SLOT(choose_char()));
		delete this->candidate_btns[i];
		this->candidate_btns[i] = NULL;
	}
	QObject::disconnect(this->up, SIGNAL(clicked()), this,
		SLOT(turnpageup()));
	delete this->up;
	this->up = NULL;
	QObject::disconnect(this->down, SIGNAL(clicked()), this,
		SLOT(turnpagedown()));
	delete this->down;
	this->down = NULL;
	QObject::disconnect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)),
		this, SLOT(restore_last_focus(QWidget*, QWidget*)));

	if (this->recognizer) {
		zinnia_recognizer_t * d = this->recognizer;
		this->recognizer = NULL;
		(void)zinnia_recognizer_close(d);
		(void)zinnia_recognizer_destroy(d);
	}

	if (this->master_layout) {
		QVBoxLayout * d = this->master_layout;
		this->master_layout = NULL;
		delete d;
	}

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__ << "DONE";
#	endif /* if defined(PIMETHOD_DEBUG) */

	rel_lock((this->access));/**/
} /* zinnia_handwriting::~zinnia_handwriting */


void zinnia_handwriting::restore_last_focus (QWidget * oldf,
	QWidget * newf) {
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	if ((newf != 0) && (! this->is_self(newf))) {
		/* when exit newf NULL -- XXX-FIXED */
		this->focus = newf;
	}
	if (newf != 0) {
		/* when exit newf NULL -- XXX-FIXED */
#		if defined(PIMETHOD_DEBUG)
		qDebug() << __FILE__ << __LINE__ << __func__ << "RESTORE";
#		endif /* if defined(PIMETHOD_DEBUG) */
		this->focus->setFocus();
	}
} /* zinnia_handwriting::restore_last_focus */


bool zinnia_handwriting::is_self (const QWidget * w) {
	if ((long int)w == (long int)this) {
		return true;
	}

	for (int i = 0; i < ZINNIA_HW_CB_NUM; ++i) {
		if ((long int)w == (long int)candidate_btns[i]) {
			return true;
		}
	}

	if ((long int)w == (long int)up) {
		return true;
	}

	if ((long int)w == (long int)down) {
		return true;
	}

	return false;
} /* zinnia_handwriting::is_self */


void zinnia_handwriting::mousePressEvent (QMouseEvent *evt) {
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	if (this->mouse_state == inputmethod_manager::click_state_choose) {
		this->strokes.clear();
		this->mouse_state = inputmethod_manager::click_state_press;
		this->recogtimer.stop();
		update();
		return;
	}

	this->mouse_state = inputmethod_manager::click_state_press;
	this->recogtimer.stop();

	foreach(QPoint* point, currentstroke.points) {
		delete point;
	}
	this->currentstroke.points.clear();

	this->currentstroke.segments = 1;
	QPoint * point = new QPoint;
	point->setX(evt->x());
	point->setY(evt->y());
	this->currentstroke.points.append(point);
} /* zinnia_handwriting::mousePressEvent */


void zinnia_handwriting::mouseMoveEvent (QMouseEvent *evt)
{
	static int count = 0;

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	if ((inputmethod_manager::click_state_press
		| inputmethod_manager::click_state_move) == mouse_state) { /* 鼠标按下状态 */
		QPoint* point = new QPoint;
		point->setX(evt->x());
		point->setY(evt->y());
		this->currentstroke.points.append(point);
		this->currentstroke.segments++;
	}
	mouse_state = inputmethod_manager::click_state_move;
	++count;
	if (0 == (count % ZINNIA_HW_CB_NUM)) {
		update();
	}
} /* zinnia_handwriting::mouseMoveEvent */


void zinnia_handwriting::mouseReleaseEvent (QMouseEvent *evt) {
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	if (inputmethod_manager::click_state_move != mouse_state) {
#	if defined(PIMETHOD_DEBUG)
		qDebug() << __FILE__ << __LINE__ << __func__ << "NOT MOVING";
#	endif /* if defined(PIMETHOD_DEBUG) */
		return;
	}

	/*
	 * ready for recognition
	 */
	this->append_stroke(currentstroke);
	update();
	this->mouse_state = inputmethod_manager::click_state_release;

	this->currentstroke.segments = 0;
	foreach(QPoint* point,currentstroke.points) {
		delete point;
	}
	this->currentstroke.points.clear();

	this->recogtimer.setInterval(1e3);/* 1 s */
	this->recogtimer.disconnect();
	QObject::connect(&(this->recogtimer), SIGNAL(timeout()),
		this, SLOT(recognize()));
	this->recogtimer.start();
} /* zinnia_handwriting::mouseReleaseEvent */


void zinnia_handwriting::append_stroke (LineStroke& stroke) {
	LineStroke s;
	s.segments = stroke.segments;

#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	if (stroke.segments) {
		for(int i=0; i < stroke.points.count(); i++) {
			QPoint* point = new QPoint;
			point->setX(stroke.points[i]->x());
			point->setY(stroke.points[i]->y());
			s.points.append(point);
		}
		strokes.append(stroke);
	}
} /* zinnia_handwriting::append_stroke */


void zinnia_handwriting::genbuttonstate (void) {
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	this->up->hide();
	this->down->hide();
	for (int i=0; i < ZINNIA_HW_CB_NUM; i++) {
		candidate_btns[i]->hide();
		if (index<allpage) {
			if (index * ZINNIA_HW_CB_NUM + i < dstr.count()) {
				candidate_btns[i]->setText(
					dstr[index * ZINNIA_HW_CB_NUM + i]);
				candidate_btns[i]->show();
				this->up->show();
				this->down->show();
			}
		}
	}
} /* zinnia_handwriting::genbuttonstate */


void zinnia_handwriting::turnpageup (void) {
	if (index>0) {
		index--;
	}
	genbuttonstate();
} /* zinnia_handwriting::turnpageup */


void zinnia_handwriting::turnpagedown (void) {
	if (index<allpage-1) {
		index++;
	}
	genbuttonstate();
} /* zinnia_handwriting::turnpagedown */


void zinnia_handwriting::choose_char (void) {
	static char c[6 + 1];
	static QString chose;

	/*
	QPushButton* push=static_cast<QPushButton *>(sender());
	emit routestring(push->text()[0]);
	*/

	QPushButton * b = static_cast<QPushButton *>(sender());
	chose.clear();
	chose = b->text();
	memset(c, 0, 6 + 1);
	qstrncpy(c, chose.toUtf8().data(), 6);

	/*
	 * POST OUT
	 */
	emit char_chose(this->focus, c);

	fprintf(stdout, "SELECTED: %s\n", c);
	fflush(stdout);

	this->up->hide();
	this->down->hide();
	for (int i=0; i < ZINNIA_HW_CB_NUM;i++) {
		candidate_btns[i]->hide();
	}
} /* zinnia_handwriting::choose_char */


void zinnia_handwriting::stok2qchar (void) {
	zinnia_result_t *result;
	LineStroke * l;
	character = zinnia_character_new();

	dstr.clear();
	zinnia_character_clear(character);
	zinnia_character_set_width(character, writor_width_fixed);
	zinnia_character_set_height(character, writor_height_fixed);

	for(int i=0; i < strokes.size(); i++) {
		l = &strokes[i];

		for (int j= 0; j < (l->segments); j += ((l->segments / 10) + 1)) {
			l->points[j]->x(), l->points[j]->y();
			// if(j>2)
			// 	j=l->segments-1;
			zinnia_character_add(character, i, l->points[j]->x(),
				l->points[j]->y());
		}
	}

	result = zinnia_recognizer_classify(recognizer, character, 100);
	if (result == NULL) {
		fprintf(stderr, "%s\n", zinnia_recognizer_strerror(recognizer));
		zinnia_character_destroy(character);
		// zinnia_recognizer_destroy(recognizer);
		return ;
	}

	int n = (int)zinnia_result_size(result);
	for (int i = 0; i < n; ++i) {
		dstr.append(QString::fromUtf8(zinnia_result_value(result, i), 3));
		// strcpy(c->charactor,zinnia_result_value(result, i));
#		if defined(PIMETHOD_DEBUG)
		fprintf(stdout, "%s\t%f\n", zinnia_result_value(result, i),
				zinnia_result_score(result, i));
#		endif /* if defined(PIMETHOD_DEBUG) */
	}

	zinnia_result_destroy(result);
	zinnia_character_destroy(character);

	return;
} /* zinnia_handwriting::stok2qchar */


void zinnia_handwriting::recognize (void) {
#	if defined(PIMETHOD_DEBUG)
	qDebug() << __FILE__ << __LINE__ << __func__;
#	endif /* if defined(PIMETHOD_DEBUG) */

	this->recogtimer.stop();
	mouse_state = inputmethod_manager::click_state_choose;
	/*
	 * real recognize
	 */
	stok2qchar();
	strokes.clear();
	index = 0;

	/* 向上取整 实现方法 */
	allpage = ((dstr.count() % ZINNIA_HW_CB_NUM) == 0)
		? (dstr.count()) / ZINNIA_HW_CB_NUM
		: (dstr.count()) / ZINNIA_HW_CB_NUM + 1;
	genbuttonstate();
	update();
} /* zinnia_handwriting::recognize */


void zinnia_handwriting::paintEvent (QPaintEvent *evt) {
	QPainter painter(this);

	/*
	painter.drawLine(0, 125, 250, 125);
	painter.drawLine(125, 0, 125, 250);
	*/
	/* - */
	QPen pen_b;
	pen_b.setStyle(Qt::DotLine);
	painter.setPen(pen_b);
	painter.drawLine(0, zinnia_handwriting::writor_height_fixed/2,
		zinnia_handwriting::writor_width_fixed,
		zinnia_handwriting::writor_height_fixed/2);
	/* | */
	painter.drawLine(zinnia_handwriting::writor_width_fixed/2, 0,
		zinnia_handwriting::writor_width_fixed/2,
		zinnia_handwriting::writor_height_fixed);

	QPen pen;
	pen.setWidth(zinnia_handwriting::writor_pen_w_fixed);
	painter.setPen(pen);

	// painter.save();
	// painter.restore();

	LineStroke *cl;
	// 已经录入的笔画
	int i = 0;
	for (i = 0; i < strokes.count() ; i++ ) {
		cl =&strokes[i];

		for(int j = 0 ;j+4 < cl->segments ; j += 4) {
			painter.drawLine((float)cl->points[j]->x(),
				(float)cl->points[j]->y(),
				(float)cl->points[j+4]->x(),
				(float)cl->points[j+4]->y());
		}
	}

	// 当下笔画
	if (currentstroke.segments &&currentstroke.points[0]) {
		for (int j = 0 ; j + 4 < currentstroke.segments ; j += 4) {
			painter.drawLine((float)currentstroke.points[j]->x(),
				(float)currentstroke.points[j]->y(),
				(float)currentstroke.points[j+4]->x(),
				(float)currentstroke.points[j+4]->y());
		}
	}

	/*
	 * when last NOT hide yet
	 */
	if ((i > 0) && (! this->up->isHidden())) {
		this->up->hide();
		this->down->hide();
		for (i = 0; i < ZINNIA_HW_CB_NUM; ++i) {
			this->candidate_btns[i]->hide();
		}
	}
} /* zinnia_handwriting::paintEvent */


/*
 * end of file zinnia_handwriting.cpp
 */
